function greeter(person:string) {
  return 'hello,' + person
}


const user = "Jane"
const sum = (a: number) => (b: number) => a + b
// 等价于
const sum2 = function (a:number){
  return function add(b:number){
    return a + b
  }
}

console.log(sum(1)(2))

const a:void = undefined //只能把undefined赋值给void

function v():void{ // 无返回值（即返回undefined）的funcition返回类型也是void
  console.log(123)
}

function s():string{ //一个返回string的function
  return ''
}

//undefined 和null是任何类型的子类型,默认情况下null和undefined可以赋值给任何类型的变量，
// 但是项目开启 --strictNullChecks 检测后，null 和 undefined 只能赋值给 any和本身
let undf:undefined = undefined

let nul:null = null

let num:number 
num = 1

const max=BigInt(Number.MAX_SAFE_INTEGER);
const max1 = max + 1n
const max2 = max + 2n

console.log(max1 ===max2);

const sym1 = Symbol('key1');
const sym2 = Symbol('key2');
// console.log(sym1 === sym2); 提示：此条件将始终返回 "false"，因为类型 "typeof sym1" 和 "typeof sym2" 没有重叠。
console.log(Symbol('key1') === Symbol('key1') ); //false

// 其他类型 any、unknown、never
// any和unknown都可以赋值任何类型，但是在进行具体赋值前，any类型可以进行引用或调用(编译不报错，运行报错)，但是unknown类型不允许
// 因此unknown更安全
let unkwn:unknown;
// unkwn();  // ERROR

let ay:any
// ay();

// never 永远不存在的值的类型，never 类型是任何类型的子类型，也可以赋值给任何类型；然而，没有类型是 never 的子类型或可以赋值给 never 类型（除了never本身之外）。
// 抛出异常的函数永远不会有返回值
function error(message: string): never {
  throw new Error(message);
}

// 空数组，而且永远是空的
const empty: never[] = []

// 数组
const list: Array<number> = [1, 2, 3]
// 等价于
const list2: number[] = [1, 2, 3]
//元组,元组中包含的元素，必须与声明的类型一致，而且不能多、不能少，甚至顺序不能不符
let x:[string,number]
// 元组可以push元素，但是访问新加元素会报错

// 枚举，从0开始递增
enum Direction {
  Up,
  Down,
  Left,
  Right
}

console.log(Direction.Up,Direction.Down,Direction.Left,Direction.Right);
// 0 1 2 3

// 对第一个值进行赋值会导致后面的值依次增加
enum Direction2 {
  Up=5,
  Down,
  Left,
  Right
}
console.log(Direction2.Up,Direction2.Down,Direction2.Left,Direction2.Right);
// 5 6 7 8

// 也可以是字符串枚举
enum Direction3 {
  Up='Up',
  Down='Down',
  Left='Left',
  Right='Right'
}

//也可以使用异构枚举，但不推荐

// 枚举的反向映射
console.log(Direction[0]) // Up

// 常量枚举
const enum Direction4 {
  Up = 'Up',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right'
}

const up = Direction4.Up
// 编译结果
// const up = 'Up' //ts的性能优化方案，会把枚举格式去掉

declare let dir:Direction4 //定义联合类型后，dir变量只能使用枚举赋值

// !!??存疑
// dir = Direction4.Down
// dir = 'Up' //ERROR:不能将类型“"Up"”分配给类型“Direction4”。

// 枚举合并,两个相同命名的枚举定义会合并
enum Direction{
  Other = 'Other'
}

console.log(Direction.Other,Direction.Left);

// 接口：interface
interface Say {
  (words: string) : string //指定接收string，返回一个string的function
}

interface User {
  name: string
  age?: number // 可选属性 即 number|undefined
  readonly isMale: boolean //只读属性
  //定义function
  say: (words: string) => string //方法1
  say2: Say
}

const getUserName = (user:User) => {
  // user.isMale = false // ERROR:无法分配到 "isMale" ，因为它是只读属性
}

// 动态接口，以下接口拥有无限属性数量，除了width以外其他属性不关注类型
interface Config {
  width?: number;
  [propName: string]: any;
}

let config:Config = {
  height: 123, //ok
  // width: '233' // ERROR
}

// 可索引类型
interface Phone {
  [name: string] : string
}

interface User {
  phone: Phone // phone对象的属性个数为任意个，但属性名称和属性值皆为string
}

interface Gender {
  sex: number
}
// 继承接口,单个或多个
interface VIPUser extends User,Gender {
  broadcast: () => void
}

let vip:VIPUser = {
  phone: {
    name: 'sdsad'
  },
  sex: 1,
  broadcast: () => {},
  name: '',
  isMale: true,
  age: 1,
  say: () => '',
  say2: () => ''
}

// 抽象类-作为其他派生类的基类使用，不能被直接实例化
abstract class Animal {
  constructor(parameters:any) {
    console.log(parameters);
  }
  move():void{
    console.log('run and run');
  }
  protected run(){
    console.log('I like run!!');
  }
  private aniRun(){
    console.log('Ani run '); 
  }
}

// let animal = new Animal() //Error:无法创建抽象类的实例
class Cat extends Animal {
  color:string = 'red'
  public say(){
    console.log('miao!');
  }
  private iSay(){
    console.log('i say');
  }
  callSay(){
    this.iSay() //只能内部访问
  }
  callRun(){
    this.run(); // 子类可以访问protected的方法
    // this.aniRun(); //ERROR: 属性“aniRun”为私有属性，只能在类“Animal”中访问
  }
}

let cat = new Cat('this is a cat')
// 限定修饰符 public、private、protected
// 默认为public 允许外部访问
cat.say()
cat.move()

// private 只可以被内部访问
// cat.iSay() //ERROR:属性“iSay”为私有属性，只能在类“Cat”中访问
cat.callSay()

// protected 只可以被类的内部以及类的子类访问
cat.callRun()
// cat.run() //ERROR:属性“run”受保护，只能在类“Animal”及其子类中访问

// class 作为接口：待补充



//function
const numSum = (a:number, b?:number) => a + (b?b:0) 
//ts编译过程会对numSum的返回结果的类型进行推断，得出number

// 剩余参数的使用
// 累加器
const add = (a: number, ...rest: number[]) => rest.reduce(((a, b) => a + b), a)
// ...rest把剩余的所有参数以数组的形式保存到形参rest里面
console.log(add(1,2,3,4,5,6,7));

// 重载，为了精确定义输入类型和输出类型，亦可清楚定义不同的输入参数数量，方便多人协作场景
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: string, y: string): string;
function reverse(x: number | string, y?:string): number | string | void {
    if (y != undefined) {
      return y
    }
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

// 泛型
// 捕获入参类型，以T表示
function returnItem<T>(para: T): T {
  return para
}
// 多参数
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
// 泛型作为类型的一部分
function getArrayLength<T>(arg: Array<T>) {
  console.log(arg.length) // ok
  return arg
}
getArrayLength([1,23,4])

// 泛型接口
interface MyItemFn<T>{
  (para: T): T
}
const myItem:MyItemFn<number> = param => param
console.log(myItem(12342424));

// 泛型类
class Stack<T> {
  private arr: T[] = []

  public push(item: T) {
      this.arr.push(item)
  }

  public pop() {
      this.arr.pop()
  }
}

const stack1 = new Stack<number>()

// 泛型约束 -- 指定接收的类型范围
type Params = number | string
class Stack2<T extends Params> {
  private arr: T[] = []

  public push(item: T) {
      this.arr.push(item)
  }

  public pop() {
      this.arr.pop()
  }
}

// const stack2 = new Stack2<boolean>() //Error:类型“boolean”不满足约束“Params”
const stack2 = new Stack2<string>() //ok

// 索引类型--取指定对象的属性作为泛型约束的选项 keyof，确保该传入参数值在对象的属性列表范围内
function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
  return obj[key] // ok
}

// console.log(getValue({a: 1}, 'b')); //ERROR:类型“"b"”的参数不能赋给类型“"a"”的参数。ts(2345)
console.log(getValue({a: 1}, 'a')); //  1

// 类型断言 as
interface Person {
  name: string;
  age: number;
}

const person = {} as Person;
// 双重断言
const person2 = 'xiaomuzhu' as any as Person; // ok，先断言为any再断言为Person是可行的，但是不推荐使用

// 类型守卫,缩短类型范围：instanceof,in,字面量类型守卫
// instanceof 类型保护
class Person {
  name = 'xiaomuzhu';
  age = 20;
}

function getSometing(arg: Person | Cat) {
  // 类型细化为 Person
  if (arg instanceof Person) {
      // console.log(arg.color); // Error，因为arg被细化为Person，而Person上不存在 color属性
      console.log(arg.age); // ok
  }
  // 类型细化为 Person
  if (arg instanceof Cat) {
      // console.log(arg.age); // Error，因为arg被细化为Cat，而Cat上不存在 age 属性
      console.log(arg.say); // ok
  }
}

//in
function getSometing1(arg: Person | Cat) {
	if ('age' in arg) {
		// console.log(arg.color); // Error:类型“Person”上不存在属性“color”
		console.log(arg.age); // ok
	}
	if ('color' in arg) {
		// console.log(arg.age); // Error:类型“Cat”上不存在属性“age”
		console.log(arg.color); // ok
	}
}

// 字面量类型守卫
type Foo = {
  kind: 'foo'; // 字面量类型
  foo: number;
};

type Bar = {
  kind: 'bar'; // 字面量类型
  bar: number;
};

function doStuff(arg: Foo | Bar) {
  if (arg.kind === 'foo') {
    console.log(arg.foo); // ok
    // console.log(arg.bar); // Error：类型“Foo”上不存在属性“bar”
  } else {
    // console.log(arg.foo); // Error：类型“Bar”上不存在属性“foo”
    console.log(arg.bar); // ok
  }
}

